//
// Filename: Mercator_To_Gdc_Converter.java
//
// Author: Jesus Diaz Centeno, Batmap S.A. <URL:mailto:jesus.diaz@batmap.com>
// <URL:http://www.batmap.com>
// Package: GeoTransform <http://www.ai.sri.com/geotransform/>
//
// Acknowledgements:
//   The algorithms used in the package were created by Jesus Diaz Centeno.
//
// License:
//   The contents of this file are subject to GeoTransform License Agreement
//   (the "License"); you may not use this file except in compliance with
//   the License. You may obtain a copy of the License at
//   http://www.ai.sri.com/geotransform/license.html
//
//   Software distributed under the License is distributed on an "AS IS"
//   basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
//   the License for the specific language governing rights and limitations
//   under the License.
//
//   Portions are Copyright (c) Batmap S.A. 2002.
//

package geotransform.transforms;

import java.lang.*;
import geotransform.ellipsoids.*;
import geotransform.coords.*;

/*
 * Class: Mercator_To_Gdc_Converter
 *
 * Description: *//**
 *   Converts Mercator coordinate(s) to GDC.
 *
 *   This class provides the capability to convert from
 *   Mercator coordinates to
 *   geodetic (GDC) lat/long.
 *   Methods are provided to convert either a single point
 *   or an array of points. This is a direct conversion.
 *
  * @author Jesus Diaz Centeno, Batmap S.A
 *
 * @version $Id: Mercator_To_Gdc_Converter.java,v 1.1 2002/01/24 06:32:12 reddy Exp $
 *
 * @see Gcc_To_Gdc_Converter
 * @see Gcc_To_UTM_Converter
 * @see Mercator_To_Gcc_Converter
 * @see Mercator_To_Gdc_Converter
 * @see Gdc_To_Gcc_Converter
 * @see Gdc_To_Mercator_Converter
 * @see Gdc_To_Mercator_Converter
 */

public class Mercator_To_Gdc_Converter
{
    static final double DEGREES_PER_RADIAN = 57.295779513082320876798154814105;

    static double a,
                  f,
                  b,
                  Eps2,
                  e1,EE2,EE3,
                  Epps2;

   /*
    * Method: Init
    *
    * Description: *//**
    *   Initializes the class for a specific ellipsoid.
    *
    *   This method will set up various internal variables
    *   that are used to perform the coordinate conversions.
    *   You need to supply an ellipsoid to initialise these
    *   conversions.
    *   <p>
    *   You MUST call one of the Init methods before calling any of
    *   the Convert methods.
    *
    * @param a   the semi-major axis (meters) for the ellipsoid
    * @param f   the inverse flattening for the ellipsoid
    *
    * @return void
    *
    * @since  1.0
    *
    */

    public static void Init(double a, double f)
    {
        CreateConstants(a,f);
    }

   /*
    * Method: Init
    *
    * Description: *//**
    *   Initializes the class for a specific ellipsoid.
    *
    *   This method will set up various internal variables
    *   that are used to perform the coordinate conversions.
    *   The WGS 84 ellipsoid will be assumed.
    *   <p>
    *   You MUST call one of the Init methods before calling any of
    *   the Convert methods.
    *
    * @return void
    *
    * @since  1.0
    *
    */

    public static void Init()
    {
        CreateConstants(6377563.396,299.3249646); // default to WGS84
    }

   /*
    * Method: Init
    *
    * Description: *//**
    *   Initializes the class for a specific ellipsoid.
    *
    *   This method will set up various internal variables
    *   that are used to perform the coordinate conversions.
    *   You need to supply an ellipsoid to initialise these
    *   conversions.
    *   <p>
    *   You MUST call one of the Init methods before calling any of
    *   the Convert methods.
    *
    * @param E   an Ellipsoid instance for the ellipsoid, e.g. WE_Ellipsoid
    *
    * @return void
    *
    * @since  1.0
    *
    * @see geotransform.ellipsoids.Ellipsoid
    *
    */

    public static void Init(Ellipsoid E)
    {
        CreateConstants(E.a,E.f);
    }

    protected static void CreateConstants(double A, double F)
    {

        //  Create the ERM constants.

        a      =A;
        f      =1/F;
        b      =(a) * (1-(f));

        Eps2   =(f) * (2.0-f);
        e1 = (1-Math.sqrt(1-Eps2))/(1+Math.sqrt(1-Eps2));
        EE2    =Eps2*Eps2;
        EE3    =EE2*Eps2;
        Epps2  =(Eps2) / (1.0 - Eps2);

    } // end init

   /*
    * Method: Convert
    *
    * Description: *//**
    *   Performs a single coordinate transform.
    *
    *   This function will transform a single coordinate. The
    *   input coordinate is provided in the Mercator_Coord_3d instance.
    *   The result of the transformation will be put in the
    *   Gdc_Coord_3d instance.
    *
    * @param Mercator_coord   the input Mercator coordinate
    * @param gdc_coord        the output GDC coordinate
    *
    * @return void
    *
    * @since  1.0
    *
    * @see geotransform.coords.Mercator_Coord_3d
    * @see geotransform.coords.Gdc_Coord_3d
    *
    */

    public static void Convert(Mercator_Coord_3d Mercator_coord, Gdc_Coord_3d gdc_coord)
    {
        Mercator_Coord_3d Mercator[] = new Mercator_Coord_3d[1];
        Gdc_Coord_3d gdc[] = new Gdc_Coord_3d[1];

        Mercator[0] = Mercator_coord;
        gdc[0] = gdc_coord;

        Convert(Mercator,gdc);
    }

   /*
    * Method: Convert
    *
    * Description: *//**
    *   Performs multiple coordinate transforms.
    *
    *   This function will transform any array
    *   of coordinates. The
    *   input coordinate array is provided in the Mercator_Coord_3d[] instance.
    *   The results of the transformation will be put into each element
    *   of the Gdc_Coord_3d[] instance. You should have at least as
    *   many entries in the target array as exist in the source array.
    *
    * @param Mercator   the input array of Mercator coordinates
    * @param gdc        the output array of GDC coordinates
    *
    * @return void
    *
    * @since  1.0
    *
    * @see geotransform.coords.Mercator_Coord_3d
    * @see geotransform.coords.Gdc_Coord_3d
    *
    */

    public static void Convert(final Mercator_Coord_3d[] Mercator, Gdc_Coord_3d[] gdc)
    {
        double source_x, source_y,
               x ,x1 ,t;

        for(int i=0;i<gdc.length;i++)
        {

            gdc[i].elevation = Mercator[i].z;

            source_x = Mercator[i].x;
            source_y = Mercator[i].y;

            t=Math.pow(Math.E,(-source_y/a));

            x=(Math.PI/2-2*Math.atan(t));
            x1=x;
            do{
            x1=x;
            x= Math.PI/2-2*Math.atan(t*(Math.pow((1-Math.sqrt(Eps2)*Math.sin(x))/(1+Math.sqrt(Eps2)*Math.sin(x)),(Math.sqrt(Eps2)/2))));
            }while ((x-x1)>0.0000000001);


	    gdc[i].latitude = Math.PI/2-2*Math.atan(t*(Math.pow((1-Math.sqrt(Eps2)*Math.sin(x))/(1+Math.sqrt(Eps2)*Math.sin(x)),(Math.sqrt(Eps2)/2))));
	    gdc[i].latitude = gdc[i].latitude  * DEGREES_PER_RADIAN;


	    gdc[i].longitude = source_x/a;
	    gdc[i].longitude = (gdc[i].longitude) * DEGREES_PER_RADIAN;


        } // end for

    } // end Convert

} // end Mercator_To_Gdc_Converter


